﻿namespace SS.Common.Collection
{
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Runtime.ConstrainedExecution;
    using System.Runtime.InteropServices;
    using System.Runtime.Serialization;
    using System.Security;
    using System.Threading;

    /// <summary>
    /// The concurrent queue ex.
    /// </summary>
    /// <typeparam name="T">
    /// </typeparam>
    [Serializable, ComVisible(false), DebuggerDisplay("Count = {Count}")]
    public class ConcurrentQueue<T> : IConcurrentCollection<T>, IEnumerable<T>, ICollection, IEnumerable,
                                        ISerializable, IDeserializationCallback
    {
        // Fields
        /// <summary>
        /// The maximum size of segment.
        /// </summary>
        private const int SEGMENT_SIZE = 0x20;

        /// <summary>
        /// The near maximum size of segment = Max - 1
        /// </summary>
        private const int SEGMENT_SIZE_NEAR = 0x1f;

        /// <summary>
        /// Head segment in queue
        /// </summary>
        private volatile Segment<T> m_head;

        /// <summary>
        /// Serialization Info
        /// </summary>
        private SerializationInfo m_serializationInfo;

        /// <summary>
        /// Tail segment in queue
        /// </summary>
        private volatile Segment<T> m_tail;

        // Methods
        /// <summary>
        /// Initializes a new instance of the <see cref="ConcurrentQueueEx{T}"/> class.
        /// </summary>
        public ConcurrentQueue()
        {
            this.m_head = this.m_tail = new Segment<T>();
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="ConcurrentQueueEx{T}"/> class.
        /// </summary>
        /// <param name="collection">
        /// The collection.
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// </exception>
        public ConcurrentQueue(IEnumerable<T> collection)
        {
            if (collection == null)
            {
                throw new ArgumentNullException("collection");
            }
            //Initialize collection
            this.InitializeFromCollection(collection);
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="ConcurrentQueueEx{T}"/> class.
        /// </summary>
        /// <param name="info">
        /// The info.
        /// </param>
        /// <param name="context">
        /// The context.
        /// </param>
        protected ConcurrentQueue(SerializationInfo info, StreamingContext context)
        {
            this.m_serializationInfo = info;
        }

        /// <summary>
        /// Gets a value indicating whether IsEmpty.
        /// </summary>
        public bool IsEmpty
        {
            [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
            get
            {
                Segment<T> head = this.m_head;
                if (head.IsEmpty)
                {
                    //Head segment is empty, check next its link
                    if (head.Next == null)
                    {
                        return true;
                    }
                    var wait = new SpinWait();
                    while (head.IsEmpty)
                    {
                        if (head.Next == null)
                        {
                            return true;
                        }
                        wait.SpinOnce();
                        head = this.m_head;
                    }
                }
                return false;
            }
        }

        #region IDeserializationCallback Members

        /// <summary>
        /// The i deserialization callback. on deserialization.
        /// </summary>
        /// <param name="sender">
        /// The sender.
        /// </param>
        void IDeserializationCallback.OnDeserialization(object sender)
        {
            this.OnDeserialization(sender);
        }

        #endregion

        #region IProducerConsumerCollection<T> Members

        /// <summary>
        /// The copy to.
        /// </summary>
        /// <param name="array">
        /// The array.
        /// </param>
        /// <param name="index">
        /// The index.
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// </exception>
        public void CopyTo(T[] array, int index)
        {
            if (array == null)
            {
                throw new ArgumentNullException("array");
            }
            this.ToList().CopyTo(array, index);
        }

        /// <summary>
        /// The get enumerator.
        /// </summary>
        /// <returns>
        /// </returns>
        public IEnumerator<T> GetEnumerator()
        {
            //return new <GetEnumerator>d__0<T>(0) { <>4__this = this };
            //Not implemented
            return null;
        }

        /// <summary>
        /// Try add.
        /// </summary>
        /// <param name="item">
        /// The item.
        /// </param>
        /// <returns>
        /// The producer consumer collection
        /// </returns>
        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
        bool IConcurrentCollection<T>.Add(T item)
        {
            this.Enqueue(item);
            return true;
        }

        /// <summary>
        /// Try take.
        /// </summary>
        /// <param name="item">
        /// The item.
        /// </param>
        /// <returns>
        /// The producer consumer collection.
        /// </returns>
        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
        bool IConcurrentCollection<T>.Remove(out T item)
        {
            return this.TryDequeue(out item);
        }

        /// <summary>
        /// Copy to collection.
        /// </summary>
        /// <param name="array">
        /// The array.
        /// </param>
        /// <param name="index">
        /// The index.
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// </exception>
        void ICollection.CopyTo(Array array, int index)
        {
            if (array == null)
            {
                throw new ArgumentNullException("array");
            }
            //this.ToList().CopyTo(array, index);
        }

        /// <summary>
        /// Get enumerator.
        /// </summary>
        /// <returns>
        /// IEnumerator
        /// </returns>
        IEnumerator IEnumerable.GetEnumerator()
        {
            return this.GetEnumerator();
        }

        /// <summary>
        /// The to array.
        /// </summary>
        /// <returns>
        /// </returns>
        public T[] ToArray()
        {
            return this.ToList().ToArray();
        }

        // Properties
        /// <summary>
        /// Gets Count.
        /// </summary>
        public int Count
        {
            [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
            get
            {
                Segment<T> segment;
                Segment<T> segment2;
                int num;
                int num2;
                this.GetHeadTailPositions(out segment, out segment2, out num, out num2);
                if (segment == segment2)
                {
                    return ((num2 - num) + 1);
                }
                int num3 = SEGMENT_SIZE - num;
                for (Segment<T> segment3 = segment.Next; segment3 != segment2; segment3 = segment3.Next)
                {
                    num3 += SEGMENT_SIZE;
                }
                return (num3 + (num2 + 1));
            }
        }

        /// <summary>
        /// Gets a value indicating whether ICollection.IsSynchronized.
        /// </summary>
        bool ICollection.IsSynchronized
        {
            get { return false; }
        }

        /// <summary>
        /// Gets ICollection.SyncRoot.
        /// </summary>
        object ICollection.SyncRoot
        {
            get { return null; }
        }

        #endregion

        #region ISerializable Members

        /// <summary>
        /// The i serializable. get object data.
        /// </summary>
        /// <param name="info">
        /// The info.
        /// </param>
        /// <param name="context">
        /// The context.
        /// </param>
        [SecurityCritical]
        void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
        {
            this.GetObjectData(info, context);
        }

        #endregion

        /// <summary>
        /// The enqueue.
        /// </summary>
        /// <param name="item">
        /// The item.
        /// </param>
        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
        public void Enqueue(T item)
        {
            var wait = new SpinWait();
            while (!this.m_tail.TryAppend(item, ref this.m_tail))
            {
                //Try add next times in case don't add successfully
                wait.SpinOnce();
            }
        }

        /// <summary>
        /// The get head tail positions.
        /// </summary>
        /// <param name="head">
        /// The head.
        /// </param>
        /// <param name="tail">
        /// The tail.
        /// </param>
        /// <param name="headLow">
        /// The head low.
        /// </param>
        /// <param name="tailHigh">
        /// The tail high.
        /// </param>
        private void GetHeadTailPositions(out Segment<T> head, out Segment<T> tail, out int headLow, out int tailHigh)
        {
            head = this.m_head;
            tail = this.m_tail;
            headLow = head.Low;
            tailHigh = tail.High;
            var wait = new SpinWait();
            while (((head != this.m_head) || (tail != this.m_tail)) ||
                   ((headLow != head.Low) || (tailHigh != tail.High)))
            {
                wait.SpinOnce();
                head = this.m_head;
                tail = this.m_tail;
                headLow = head.Low;
                tailHigh = tail.High;
            }
        }

        /// <summary>
        /// The get object data.
        /// </summary>
        /// <param name="info">
        /// The info.
        /// </param>
        /// <param name="context">
        /// The context.
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// </exception>
        [SecurityCritical]
        protected virtual void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            if (info == null)
            {
                throw new ArgumentNullException("info");
            }
            info.AddValue("Data", this.ToArray(), typeof (T[]));
        }

        /// <summary>
        /// The initialize from collection.
        /// </summary>
        /// <param name="collection">
        /// The collection.
        /// </param>
        private void InitializeFromCollection(IEnumerable<T> collection)
        {
            this.m_head = this.m_tail = new Segment<T>();
            int num = 0;
            //Fill data from collection to Queue
            foreach (T local in collection)
            {
                if (num >= SEGMENT_SIZE)
                {
                    //Create new segment if tail segment is full (SEGMENT_SIZE item)
                    this.m_tail = this.m_tail.UnsafeGrow();
                    num = 0;
                }
                this.m_tail.UnsafeAdd(local);
                num++;
            }
        }

        /// <summary>
        /// The on deserialization.
        /// </summary>
        /// <param name="sender">
        /// The sender.
        /// </param>
        protected virtual void OnDeserialization(object sender)
        {
            if (this.m_serializationInfo != null)
            {
                var collection = this.m_serializationInfo.GetValue("Data", typeof (T[])) as T[];
                if (collection == null)
                {
                    throw new SerializationException("ConcurrentStackQueue_OnDeserialization_NoData");
                }
                //Initialize Collection
                this.InitializeFromCollection(collection);
            }
        }

        /// <summary>
        /// The to list.
        /// </summary>
        /// <returns>
        /// </returns>
        private List<T> ToList()
        {
            Segment<T> segment;
            Segment<T> segment2;
            int num;
            int num2;
            this.GetHeadTailPositions(out segment, out segment2, out num, out num2);
            if (segment == segment2)
            {
                return segment.ToList(num, num2);
            }
            var list = new List<T>(segment.ToList(num, SEGMENT_SIZE_NEAR));
            for (Segment<T> segment3 = segment.Next; segment3 != segment2; segment3 = segment3.Next)
            {
                list.AddRange(segment3.ToList(0, SEGMENT_SIZE_NEAR));
            }
            list.AddRange(segment2.ToList(0, num2));
            return list;
        }

        /// <summary>
        /// The try dequeue.
        /// </summary>
        /// <param name="result">
        /// The result.
        /// </param>
        /// <returns>
        /// The try dequeue.
        /// </returns>
        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
        public bool TryDequeue(out T result)
        {
            while (!this.IsEmpty)
            {
                if (this.m_head.TryRemove(out result, ref this.m_head))
                {
                    return true;
                }
            }
            result = default(T);
            return false;
        }

        /// <summary>
        /// The try peek.
        /// </summary>
        /// <param name="result">
        /// The result.
        /// </param>
        /// <returns>
        /// The try peek.
        /// </returns>
        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
        public bool TryPeek(out T result)
        {
            while (!this.IsEmpty)
            {
                if (this.m_head.TryPeek(out result))
                {
                    return true;
                }
            }
            result = default(T);
            return false;
        }

        #region Nested type: Segment

        /// <summary>
        /// The segment.
        /// </summary>
        /// <typeparam name="T">
        /// </typeparam>
        private class Segment<T>
        {
            // Fields
            /// <summary>
            /// Array store data, with size = SEGMENT_SIZE
            /// </summary>
            internal volatile T[] m_array;

            /// <summary>
            /// 
            /// </summary>
            private volatile int m_high;

            /// <summary>
            /// 
            /// </summary>
            private volatile int m_low;

            /// <summary>
            /// Link to next segment
            /// </summary>
            private volatile ConcurrentQueue<T>.Segment<T> m_next;

            /// <summary>
            /// 
            /// </summary>
            private volatile int[] m_state;

            // Methods
            /// <summary>
            /// Initializes a new instance of the <see cref="Segment{T}"/> class.
            /// </summary>
            internal Segment()
            {
                this.m_array = new T[SEGMENT_SIZE];
                this.m_state = new int[SEGMENT_SIZE];
                this.m_next = null;
                this.m_low = 0;
                this.m_high = -1;
            }

            /// <summary>
            /// Gets High.
            /// </summary>
            internal int High
            {
                get { return Math.Min(this.m_high, SEGMENT_SIZE_NEAR); }
            }

            /// <summary>
            /// Gets a value indicating whether IsEmpty.
            /// </summary>
            internal bool IsEmpty
            {
                get { return (this.Low > this.High); }
            }

            /// <summary>
            /// Gets Low.
            /// </summary>
            internal int Low
            {
                get { return Math.Min(this.m_low, SEGMENT_SIZE); }
            }

            /// <summary>
            /// Gets Next.
            /// </summary>
            internal ConcurrentQueue<T>.Segment<T> Next
            {
                get { return this.m_next; }
            }

            /// <summary>
            /// The grow.
            /// </summary>
            /// <param name="tail">
            /// The tail.
            /// </param>
            internal void Grow(ref ConcurrentQueue<T>.Segment<T> tail)
            {
                this.m_next = new ConcurrentQueue<T>.Segment<T>();
                tail = this.m_next;
            }

            /// <summary>
            /// The to list.
            /// </summary>
            /// <param name="start">
            /// The start.
            /// </param>
            /// <param name="end">
            /// The end.
            /// </param>
            /// <returns>
            /// </returns>
            internal List<T> ToList(int start, int end)
            {
                var list = new List<T>();
                for (int i = start; i <= end; i++)
                {
                    var wait = new SpinWait();
                    while (this.m_state[i] == null)
                    {
                        wait.SpinOnce();
                    }
                    list.Add(this.m_array[i]);
                }
                return list;
            }

            /// <summary>
            /// The try append.
            /// </summary>
            /// <param name="value">
            /// The value.
            /// </param>
            /// <param name="tail">
            /// The tail.
            /// </param>
            /// <returns>
            /// The try append.
            /// </returns>
            internal bool TryAppend(T value, ref ConcurrentQueue<T>.Segment<T> tail)
            {
                //Check segment is full or not
                if (this.m_high < SEGMENT_SIZE_NEAR)
                {
                    int index = SEGMENT_SIZE;
                    try
                    {
                    }
                    finally
                    {
                        //Add new item into array
                        index = Interlocked.Increment(ref this.m_high);
                        if (index <= SEGMENT_SIZE_NEAR)
                        {
                            this.m_array[index] = value;
                            this.m_state[index] = 1;
                        }
                    }
                    if (index < SEGMENT_SIZE_NEAR)
                    {
                        return true;
                    }
                    if (index == SEGMENT_SIZE_NEAR)
                    {
                        this.Grow(ref tail);
                        return true;
                    }
                }
                return false;
            }

            /// <summary>
            /// The try peek.
            /// </summary>
            /// <param name="result">
            /// The result.
            /// </param>
            /// <returns>
            /// The try peek.
            /// </returns>
            internal bool TryPeek(out T result)
            {
                result = default(T);
                int low = this.Low;
                if (low > this.High)
                {
                    return false;
                }
                //Wait data
                var wait = new SpinWait();
                while (this.m_state[low] == null)
                {
                    wait.SpinOnce();
                }
                result = this.m_array[low];
                return true;
            }

            /// <summary>
            /// The try remove.
            /// </summary>
            /// <param name="result">
            /// The result.
            /// </param>
            /// <param name="head">
            /// The head.
            /// </param>
            /// <returns>
            /// The try remove.
            /// </returns>
            internal bool TryRemove(out T result, ref ConcurrentQueue<T>.Segment<T> head)
            {
                var wait = new SpinWait();
                int low = this.Low;
                for (int i = this.High; low <= i; i = this.High)
                {
                    if (Interlocked.CompareExchange(ref this.m_low, low + 1, low) == low)
                    {
                        var wait2 = new SpinWait();
                        while (this.m_state[low] == null)
                        {
                            wait2.SpinOnce();
                        }
                        result = this.m_array[low];
                        if ((low + 1) >= SEGMENT_SIZE)
                        {
                            wait2 = new SpinWait();
                            while (this.m_next == null)
                            {
                                wait2.SpinOnce();
                            }
                            head = this.m_next;
                        }
                        return true;
                    }
                    wait.SpinOnce();
                    low = this.Low;
                }
                result = default(T);
                return false;
            }

            /// <summary>
            /// The unsafe add.
            /// </summary>
            /// <param name="value">
            /// The value.
            /// </param>
            internal void UnsafeAdd(T value)
            {
                this.m_high++;
                this.m_array[this.m_high] = value;
                this.m_state[this.m_high] = 1;
            }

            /// <summary>
            /// The unsafe grow.
            /// </summary>
            /// <returns>
            /// </returns>
            internal ConcurrentQueue<T>.Segment<T> UnsafeGrow()
            {
                var segment = new ConcurrentQueue<T>.Segment<T>();
                this.m_next = segment;
                return segment;
            }

            // Properties
        }

        #endregion
    }
}